home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 001 / commdev.arc / IBMRS232.ASM next >
Assembly Source File  |  1986-12-29  |  24KB  |  674 lines

  1.     page    60,132
  2. ;***********************************************************
  3. ;**                                                       **
  4. ;**  Device Driver for RS232 communications               **
  5. ;**  IBM PC Version                                       **
  6. ;**  Copyright (C) Texas Instruments 1986                 **
  7. ;**  Author: Greg Haley                                   **
  8. ;**                                                       **
  9. ;**  THIS SOURCE CODE MAY BE DISTRIBUTED AND MODIFIED     **
  10. ;**  ONLY IF THE ORIGINAL COPYRIGHT AND AUTHOR CREDITS    **
  11. ;**  REMAIN INTACT.                                       **
  12. ;**                                                       **
  13. ;**  Project Start Date: 11/20/86                         **
  14. ;**                                                       **
  15. ;**  Re: 12/29/86 by Greg Haley                           **
  16. ;**    Added send_xon routine.                            **
  17. ;**    Added 19200 baud.                                  **
  18. ;**                                                       **
  19. ;***********************************************************
  20.  
  21.         name    ibmrs232 
  22.         title   Device Driver for IBM PC Communications  
  23.  
  24. code    segment byte
  25.    assume cs:code,ds:nothing,es:nothing
  26.  
  27.     include    rs232.inc
  28.  
  29.     page
  30. ;***********************************************************
  31. ;**  Keyboard Routines                                    **
  32. ;***********************************************************
  33. keyboard equ    22             ; IBM PC keyboard INT
  34. altah    db    0            ; Storage for high byte of key input
  35.  
  36. k_ready:
  37.     mov    al,cs:altah        ; Get 2nd half of F-key?
  38.     or    al,al
  39.     jnz    k_rdy_xit        ; Yes, skip to end
  40.  
  41.         mov     ah,1                    ; Is a char waiting?
  42.         int     keyboard
  43.     jz    k_rdy_xit        ; No, exit
  44.  
  45.     or    ax,ax            ; If it was a ^C, remove it
  46.     jnz    k_rdy_xit
  47.     xor    ah,ah
  48.     int    keyboard
  49.     jmp    short k_ready        ; And try again
  50.  
  51. k_rdy_xit:
  52.     ret
  53.  
  54. k_read:
  55.     xor    ax,ax            ; Clear AX
  56.  
  57.     xchg    al,cs:altah        ; Get 2nd half of F-key?
  58.     or    al,al
  59.     jnz    k_r_xit            ; Yes, skip to end
  60.  
  61. k_r_1:
  62.         xor     ah,ah
  63.         int     keyboard
  64.     
  65.     or    ax,ax            ; Was it a ^C?
  66.     jz    k_r_1            ; Yes, get another
  67.  
  68.     or    al,al            ; Is it a F-key?
  69.     jnz    k_r_xit            ; No, exit
  70.     mov    cs:altah,ah        ; Yes, save 2nd half
  71.  
  72. k_r_xit:
  73.     ret
  74.  
  75.     page
  76. ;***********************************************************
  77. ;**  Communications Routines                              **
  78. ;***********************************************************
  79. recv_size       equ     800h            ; Receive buffer size
  80.                     ; Must be one of these values:
  81.                     ; 0004h    = 4 bytes
  82.                     ; 0008h    = 8 bytes
  83.                     ; 0010h    = 16 bytes
  84.                     ; 0020h    = 32 bytes
  85.                     ; 0040h    = 64 bytes
  86.                     ; 0080h    = 128 bytes
  87.                     ; 0100h    = 256 bytes
  88.                     ; 0200h    = 512 bytes
  89.                     ; 0400h    = 1024 bytes
  90.                     ; 0800h    = 2048 bytes
  91.                     ; 1000h    = 4096 bytes
  92.                     ; 2000h    = 8192 bytes
  93.                     ; 4000h    = 16384 bytes
  94.                     ; 8000h    = 32768 bytes
  95. recv_limit      equ     not recv_size   ; Receive buffer limit mask
  96. busy_len        equ     recv_size *3 /4 ; Go busy at 3/4 buf length
  97. not_busy_len    equ     recv_size /2    ; Not busy at 1/2 buf length
  98.  
  99. int_controller  equ     21h             ; 8259A Interrupt Controller port
  100. int_ack         equ     20h             ; 8259A Interrupt Acknowledge port
  101.  
  102. enable_ints     equ     00001111b       ; Enable interrupts
  103. disable_ints    equ     00000000b       ; Disable interrupts
  104. dcd_bit         equ     128             ; DCD bit in modem status register
  105. cts_bit         equ     16              ; CTS bit in modem status register
  106. dsr_bit         equ     32              ; DSR bit in modem status register
  107. ri_bit          equ     64              ; RI bit in modem status register
  108.  
  109. dtr_rts_out2    equ     00001011b       ; DTR, RTS, & OUT2
  110. no_dlab         equ     01111111b       ; Mask off DLAB bit
  111. char_waiting    equ     00000001b       ; Receive buffer full bit
  112. int_pending     equ     00000001b       ; Int pending bit in 8250
  113.  
  114. xon             equ     11h             ; xmit on busy char
  115. xoff            equ     13h             ; xmit off busy char
  116.  
  117.     page
  118. ;***********************************************************
  119. ;**     Variables                                         **
  120. ;***********************************************************
  121.  
  122. old_ss  dw      0                       ; Old SS reg
  123. old_sp  dw      0                       ; Old SP reg
  124.         db      80 dup (?)              ; Stack
  125. i_stack label   byte
  126. rq_head dw      0                       ; Receive Queue start 
  127. rq_tail dw      0                       ; Receive Queue stop 
  128. rq_len  dw      0                       ; Current receive Queue length
  129. rqueue  db      recv_size dup (?)       ; Receive queue
  130. oldseg  dw      0                       ; Old segment for int vector
  131. oldoff  dw      0                       ; Old offset for int vector
  132. busy_hand       db      0               ; busy handling type
  133. r_busy  db      0                       ; recv busy flag
  134. t_busy  db      0                       ; xmit busy flag
  135. xmit_busy       db      0               ; xmit busy flag (for ints)
  136. m_stat  db      0                       ; Current modem status
  137. l_stat  db      0                       ; Current line status
  138. dcw     dw      1100000001010000b       ; default 2400, 8, N, 1
  139. parity_on       db      0               ; Parity flag
  140.  
  141. int_tbl dw      mod_stat                ; Interrupt branch table
  142.         dw      xmit_mt                  
  143.         dw      rec_full
  144.         dw      lin_stat
  145.  
  146. ; table for baud rate constants
  147. baud_tbl        dw      417h            ; 110
  148.                 dw      300h            ; 150
  149.                 dw      180h            ; 300
  150.                 dw      0c0h            ; 600
  151.                 dw      60h             ; 1200
  152.                 dw      30h             ; 2400
  153.                 dw      18h             ; 4800
  154.                 dw      0ch             ; 9600
  155.  
  156. speed   dw      0030h                   ; Baud rate to use (2400)
  157. comm_parms      db      10000011b       ; No parity, 8 data, 1 stop
  158.  
  159. ; table for port number init tables
  160. p_table dw      port1_tbl
  161.         dw      port2_tbl
  162.  
  163. port_n  dw      port1_tbl               ; default port is 1
  164.  
  165. ; port 1 init table
  166. port1_tbl       dw      0ch             ; Interrupt vector for irq
  167.                 db      11101111b       ; Mask to enable irq
  168.                 db      00010000b       ; Mask to unable irq
  169.                 dw      3f8h            ; Receive buffer port
  170.                 dw      3f8h            ; Transmit buffer port
  171.                 dw      3f8h            ; Divisor least significant byte
  172.                 dw      3f9h            ; Divisor most significant byte
  173.                 dw      3fbh            ;  8250 UART Control port
  174.                 dw      3fdh            ;  8250 UART Status port
  175.                 dw      3fch            ;  8250 Modem Control port
  176.                 dw      3feh            ;  8250 Modem Status port
  177.                 dw      3f9h            ;  8250 interrupt enable register
  178.                 dw      3fah            ;  8250 interrupt ack register
  179.                 db      64h             ;  8259 Specific EOI
  180. p_tbl_size      equ     $-port1_tbl
  181.  
  182. ; port 2 init table
  183. port2_tbl       dw      0bh             ; Interrupt vector for irq
  184.                 db      11110111b       ; Mask to enable irq
  185.                 db      00001000b       ; Mask to unable irq
  186.                 dw      2f8h            ; Receive buffer port
  187.                 dw      2f8h            ; Transmit buffer port
  188.                 dw      2f8h            ; Divisor least significant byte
  189.                 dw      2f9h            ; Divisor most significant byte
  190.                 dw      2fbh            ;  8250 UART Control port
  191.                 dw      2fdh            ;  8250 UART Status port
  192.                 dw      2fch            ;  8250 Modem Control port
  193.                 dw      2feh            ;  8250 Modem Status port
  194.                 dw      2f9h            ;  8250 interrupt enable register
  195.                 dw      2fah            ;  8250 interrupt ack register
  196.                 db      63h             ;  8259 Specific EOI
  197.  
  198. ; defaults for port 1
  199. port_tbl        label   word
  200. comm1_vector    dw      0ch             ; Interrupt vector for irq
  201. irq_enab_mask   db      11101111b       ; Mask to enable irq
  202. irq_unab_mask   db      00010000b       ; Mask to unable irq
  203. recv_buffer     dw      3f8h            ; Receive buffer port
  204. send_buffer     dw      3f8h            ; Transmit buffer port
  205. lsb_divisor     dw      3f8h            ; Divisor least significant byte
  206. msb_divisor     dw      3f9h            ; Divisor most significant byte
  207. line_control    dw      3fbh            ;  8250 UART Control port
  208. line_status     dw      3fdh            ;  8250 UART Status port
  209. modem_control   dw      3fch            ;  8250 Modem Control port
  210. modem_status    dw      3feh            ;  8250 Modem Status port
  211. int_enable      dw      3f9h            ;  8250 interrupt enable register
  212. int_id          dw      3fah            ;  8250 interrupt ID register
  213. SEOI            db      64h             ;  8259 Specific EOI
  214.  
  215.     page
  216. ;***********************************************************
  217. ;**     Subroutine to set up comm chip per the DCW        **
  218. ;**     DCW is in AX                                      **
  219. ;***********************************************************
  220. set_dcw:
  221.  
  222. ; clear comm parms
  223.         xor     dl,dl
  224.  
  225. ; set parity type
  226.         mov     bl,al                   ; get low byte in BL
  227.         and     bl,00000011b            ; mask unused bits
  228.         or      dl,bl                   ; change parity
  229.         mov     cl,3
  230.         shl     dl,cl
  231.         and     bl,00000001b            ; change parity flag
  232.         mov     parity_on,bl            ; 
  233.  
  234. ; set num stop bits
  235.         mov     bl,al                   ; get low byte in BL
  236.         and     bl,00001000b            ; mask unused bits
  237.         shr     bl,1
  238.         or      dl,bl
  239.  
  240. ; set data bits
  241.         mov     bl,ah                   ; get high byte in BL
  242.         and     bl,01000000b            ; mask unused bits
  243.         or      bl,10000000b
  244.         mov     cl,6
  245.         shr     bl,cl
  246.         or      dl,bl
  247.  
  248. ; Save comm parms
  249.         mov     comm_parms,dl
  250.         
  251. ; set baud rate
  252.         mov     bl,al                   ; get low byte in BL
  253.         and     bl,01110000b            ; mask unused bits
  254.         mov     cl,3                    ; shift to make word ptr
  255.         shr     bl,cl
  256.         mov     si,offset baud_tbl      ; point to baud table
  257.         xor     bh,bh                   ; make BX a byte ptr
  258.         add     si,bx                   ; SI now points to baud rate const
  259.         mov     dx,word ptr [si]        ; get baud rate in DX
  260.         mov     speed,dx                ; save baud rate
  261.  
  262. ; set busy type
  263.         mov     bl,ah
  264.         and     bl,00000011b            ; mask unwanted bits
  265.         mov     byte ptr busy_hand,bl   ; store it
  266.  
  267. ; set port number
  268.         mov     bl,ah                   ; get high byte in BL
  269.         and     bl,00001000b            ; mask unused bits
  270.         shr     bl,1                    ; shift to make word ptr
  271.         shr     bl,1 
  272.         mov     si,offset p_table       ; point to port table
  273.         xor     bh,bh                   ; make BX a byte ptr
  274.         add     si,bx                   ; SI now points to baud rate const
  275.         mov     dx,word ptr [si]        ; get port adrs in DX
  276.         mov     word ptr port_n,dx      ; save port number adrs
  277.     ret
  278.  
  279.     page
  280. ;***********************************************************
  281. ;**     Subroutine to set up interrupt vector             **
  282. ;**     and initialize the  8250 comm chip                **
  283. ;***********************************************************
  284. init_comm:
  285.         push    ds
  286.         push    cs
  287.         pop     ds
  288.  
  289. ; get correct port parameters
  290.         push    es
  291.         push    cs
  292.         pop     es
  293.  
  294.         mov     si,word ptr port_n      ; get port table adrs
  295.         mov     di,offset port_tbl      ; DI points to table to use
  296.         mov     cx,p_tbl_size           ; CX has table length
  297.         repz    movsb                   ; move it
  298.         
  299.         pop     es
  300.  
  301. ; Save old int vector for irq
  302.         mov     di,comm1_vector
  303.         call    get_vector
  304.         mov     word ptr oldseg,bx
  305.         mov     word ptr oldoff,dx
  306.  
  307. ; Set up int vector for irq
  308.         push    cs                      ; Make BX = CS
  309.         pop     bx
  310.         mov     dx,offset isr
  311.         mov     di,comm1_vector
  312.         call    set_vector
  313.  
  314. ; Enable irq from 8259A
  315.         cli
  316.         in      al,int_controller
  317.         jmp     $+2                     ; delay
  318.         and     al,irq_enab_mask
  319.         out     int_controller,al
  320.         
  321. ; Set baud rate, parity, etc.
  322.         mov     dx,line_control
  323.         mov     al,comm_parms
  324.         out     dx,al
  325.         mov     dx,lsb_divisor
  326.         mov     ax,speed
  327.         out     dx,al
  328.         mov     dx,msb_divisor
  329.         mov     al,ah
  330.         out     dx,al
  331.         mov     dx,line_control
  332.         in      al,dx
  333.         and     al,no_dlab
  334.         out     dx,al
  335.  
  336. ; Read receive buffer register
  337.         call    lin_stat
  338.         test    al,char_waiting
  339.         jz      init_1
  340.         mov     dx,recv_buffer
  341.         in      al,dx
  342. init_1: 
  343.         
  344. ; Read modem control register
  345.         mov     dx,modem_control
  346.         in      al,dx
  347.         
  348. ; Read modem status register
  349.         call    mod_stat
  350.  
  351. ; Read UART status register
  352.         call    lin_stat
  353.  
  354. ; Enable 8250 interrupts
  355.         mov     dx,line_control
  356.         in      al,dx
  357.         and     al,no_dlab
  358.         out     dx,al
  359.         mov     dx,int_enable
  360.         mov     al,enable_ints
  361.         out     dx,al
  362.  
  363. ; Raise DTR, RTS, & OUT2
  364.         mov     dx,modem_control
  365.         mov     al,dtr_rts_out2
  366.         out     dx,al
  367.  
  368.         pop     ds
  369.         sti
  370.         ret
  371.  
  372.     page
  373. ;***********************************************************
  374. ;**     Subroutine to restore interrupt vector            **
  375. ;**     and reset the  8250 comm chip                     **
  376. ;***********************************************************
  377. de_init:
  378.         cli
  379.  
  380. ; Disable irq
  381.         in      al,int_controller
  382.         or      al,cs:byte ptr irq_unab_mask
  383.         jmp     $+2                     ; delay
  384.         jmp     $+2                     ; delay
  385.         out     int_controller,al
  386.  
  387. ; Disable interrupts on  8250 and drop DTR, RTS
  388.         mov     dx,cs:line_control
  389.         in      al,dx
  390.         jmp     $+2                     ; delay
  391.         and     al,no_dlab
  392.         out     dx,al
  393.         mov     dx,cs:int_enable
  394.         xor     al,al
  395.         out     dx,al
  396.  
  397. ; It's probably not a good idea to restore the vector at close in 
  398. ; this case, but here's the code to do it:
  399. ;
  400. ; Restore int vector for irq
  401. ;       mov     bx,cs:word ptr oldseg
  402. ;       mov     dx,cs:word ptr oldoff
  403. ;       mov     di,comm1_vector
  404. ;       call    set_vector
  405.         sti
  406.         ret
  407.  
  408.     page
  409. ;***********************************************************
  410. ;**     Interrupt Service Routine                         **
  411. ;***********************************************************
  412. isr:
  413.         cli
  414.         cld
  415. ; Set up new stack
  416.         mov     cs:word ptr old_sp,sp
  417.         mov     cs:word ptr old_ss,ss
  418.         mov     sp,cs
  419.         mov     ss,sp
  420.         mov     sp,offset i_stack
  421.  
  422.         push    ax
  423.         push    bx
  424.         push    cx
  425.         push    dx
  426.         push    ds
  427.  
  428.         push    cs
  429.         pop     ds
  430.  
  431. ; Verify int came from  8250
  432.         mov     dx,int_id
  433.         in      al,dx
  434.         test    al,int_pending
  435.         jnz     isr_exit
  436.  
  437. ; Branch to correct routine     
  438.         cbw                             ; make int type a word
  439.         mov     bx,offset int_tbl       ; point to int table    
  440.         add     bx,ax                   ; add int type
  441.         sti
  442.         call    cs:word ptr [bx]        ; go do subroutine
  443.         cli
  444.  
  445. isr_exit:
  446. ; Tell 8259A we're done
  447.         mov     al,SEOI
  448.         out     int_ack,al
  449.  
  450.         pop     ds
  451.         pop     dx
  452.         pop     cx
  453.         pop     bx
  454.         pop     ax
  455.  
  456. ; restore stack
  457.         mov     ss,cs:word ptr old_ss
  458.         mov     sp,cs:word ptr old_sp
  459.  
  460.         sti
  461.         iret
  462.  
  463.     page
  464. ;***********************************************************
  465. ;**     Subroutine to read the modem status               **
  466. ;***********************************************************
  467. mod_stat:
  468.         mov     dx,modem_status         ; Read modem status (CHB)
  469.         in      al,dx                   ;   into reg AL
  470.  
  471.         xor     ah,ah
  472.         test    al,ri_bit
  473.         jz      test_dsr
  474.         or      ah,4
  475. test_dsr:
  476.         test    al,dsr_bit
  477.         jz      test_cts
  478.         or      ah,1
  479. test_cts:
  480.         shl     ah,1
  481.         test    al,cts_bit
  482.         jz      test_dcd
  483.         or      ah,1
  484. test_dcd:
  485.         shl     ah,1
  486.         test    al,dcd_bit
  487.         jz      test_done
  488.         or      ah,1
  489. test_done:
  490.  
  491. ; AH now contains the line signals
  492. ; ---------------------------------
  493. ; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  494. ; ---------------------------------
  495. ; |   |   |   | RI|   |DSR|CTS|DCD|
  496. ; ---------------------------------
  497. ; 0=down, 1=up
  498.  
  499.         mov     cs:byte ptr m_stat,ah   ; Update current modem status
  500.  
  501. ; Check for busy handling
  502.         shr     ah,1                    ; DSR in bit 0
  503.         shr     ah,1
  504.         cmp     cs:byte ptr busy_hand,2 ; DSR busy handling?
  505.         jne     xit_mod_stat            ;  No, skip
  506.         mov     al,ah                   ; save AH
  507. set_tbusy:
  508.         and     al,1                    ; up = busy on
  509. ;       not     al                      ; down = busy on
  510.         mov     cs:byte ptr t_busy,al   ; set busy
  511.  
  512.  
  513. xit_mod_stat:
  514.         ret
  515.  
  516.     page
  517. ;***********************************************************
  518. ;**     Subroutine to send the next char in queue         **
  519. ;***********************************************************
  520. xmit_mt:
  521.         mov     cs:byte ptr xmit_busy,0 ; We're not busy
  522.  
  523.         ret
  524.  
  525.     page
  526. ;***********************************************************
  527. ;**     Subroutine to receive a char and queue it         **
  528. ;***********************************************************
  529. rec_full:
  530.         mov     dx,recv_buffer          ; Get char
  531.         in      al,dx
  532.         cmp     cs:byte ptr parity_on,0 ; parity?
  533.         jz      no_par                  ;   No, don't mask off parity bit
  534.         and     al,7fh                  ;   Yes, mask off parity bit
  535. no_par:
  536. ; check for XON-XOFF busy char
  537.         cmp     cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
  538.         jne     queue_char              ;  No, skip busy handling
  539.         cmp     al,xoff                 ; Need to set busy?
  540.         jne     chk_r_xon               ;  No, skip
  541.         mov     cs:byte ptr t_busy,1    ; set busy
  542.         jmp     xit_rec_full            ; We're done
  543.  
  544. chk_r_xon:
  545.         cmp     al,xon                  ; Need to reset busy?
  546.         jne     queue_char              ;  No, skip busy handling
  547.         mov     cs:byte ptr t_busy,0    ; reset busy
  548.         jmp     xit_rec_full            ; We're done
  549.  
  550. queue_char:
  551. ; check for buffer overflow
  552.         cmp     cs:word ptr rq_len,recv_limit ; buffer full?
  553.         jb      buf_full1               ; No, skip
  554.         inc     cs:word ptr rq_tail     ; Yes, lose 1 char
  555.         and     cs:word ptr rq_tail,recv_limit
  556.         dec     cs:word ptr rq_len      ; Adjust queue length
  557. buf_full1:
  558.  
  559.         mov     bx,offset rqueue        ; Queue char
  560.         mov     dx,cs:word ptr rq_head
  561.         add     bx,dx
  562.         mov     cs:byte ptr [bx],al
  563.         inc     dx
  564.         and     dx,recv_limit           ; wrap if >= receive size
  565.         mov     cs:word ptr rq_head,dx
  566.         inc     cs:word ptr rq_len      ; Adjust queue length
  567.  
  568. ; set busy if needed
  569.         cmp     cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
  570.         jne     set_rb_done             ; No, skip
  571.         cmp     cs:word ptr rq_len,busy_len ; Need to set busy?
  572.         jb      set_rb_done             ; No, skip
  573.         mov     cs:byte ptr r_busy,1    ; set busy flag
  574.         mov     dx,cs:word ptr send_buffer ; send XOFF char
  575.         mov     al,xoff
  576.         out     dx,al
  577. set_rb_done:
  578.  
  579. xit_rec_full:
  580.         ret
  581.  
  582.     page
  583. ;***********************************************************
  584. ;**     Subroutine to send an xon char if needed          **
  585. ;***********************************************************
  586. send_xon:
  587.         cmp     cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
  588.         jne     xit_send_xon            ; No, skip
  589.         mov     dx,cs:word ptr send_buffer ; send XOFF char
  590.         mov     al,xon
  591.         out     dx,al
  592.  
  593. xit_send_xon:
  594.     ret
  595.  
  596.     page
  597. ;***********************************************************
  598. ;**     Subroutine to read the line status                **
  599. ;***********************************************************
  600. lin_stat:
  601.         mov     dx,line_status
  602.         in      al,dx                   ; read status
  603.         mov     cs:byte ptr l_stat,al   ; Update current line status
  604.  
  605.         ret
  606.  
  607.     page
  608. ;***********************************************************
  609. ;**     Subroutine to get an interrupt vector             **
  610. ;**                                                       **
  611. ;**   di = vector number                                  **
  612. ;**                                                       **
  613. ;**   Return:                                             **
  614. ;**   bx = segment                                        **
  615. ;**   dx = offset                                         **
  616. ;***********************************************************
  617. get_vector:
  618.         push    es
  619.         xor     ax,ax
  620.         mov     es,ax
  621.         shl     di,1
  622.         shl     di,1
  623.         mov     dx,es:word ptr[di]
  624.         mov     bx,es:word ptr[di+2]
  625.         pop     es
  626.         ret
  627.  
  628.     page
  629. ;***********************************************************
  630. ;**     Subroutine to set an interrupt vector             **
  631. ;**                                                       **
  632. ;**   di = vector number                                  **
  633. ;**   bx = segment                                        **
  634. ;**   dx = offset                                         **
  635. ;***********************************************************
  636. set_vector:
  637.         push    es
  638.         xor     ax,ax
  639.         mov     es,ax
  640.         shl     di,1
  641.         shl     di,1
  642.         mov     es:word ptr[di],dx
  643.         mov     es:word ptr[di+2],bx
  644.         pop     es
  645.         ret
  646.  
  647.     page
  648. ;***********************************************************
  649. ;**  Everything past here is truncated after install      **
  650. ;***********************************************************
  651.  
  652. init    proc    near
  653.         lds     bx,cs:[ptrsav]
  654.         mov     word ptr [bx].trans,offset init ;set break address
  655.         mov     [bx].trans+2,cs
  656.  
  657.         push    cs
  658.         pop     ds
  659.         mov     dx,offset init_msg
  660.         mov     ah,9
  661.         int     21h
  662.  
  663.         jmp     exit
  664. init    endp
  665.  
  666. init_msg:
  667.         db      cr,lf,'IBM PC Communications Driver v2.51'
  668.         db      ' Copyright (C) Texas Instruments 1986'
  669.         db      cr,lf,'Written by '
  670.         db      'Greg Haley'
  671.         db      cr,lf,cr,lf,'$'
  672. code    ends
  673.         end
  674.